<?php defined('SYSPATH') or die('No direct script access.');

require_once APPPATH . '/libraries/recaptchalib.php';

class Auth_Controller extends PublicController {

	public function __construct() {
		parent::__construct();
	}

	public function loginUser() {
		$formData = $this->postToDbValues();
		
		// Session fixation protection
		// http://en.wikipedia.org/wiki/Session_fixation
		session_regenerate_id();

		if (valid::isWhite($formData['userLogin']) || valid::isWhite($formData['userPassword'])) {
			$this->msg->error('public.error-loginPasswordRequired');
			$this->redirectBack();
			return;
		}
		$person = $this->dao->fetchPersonByLogin($formData['userLogin']);
		
		$account = $this->dao->getAccount($person['id']);
		if ($account['last_unsuccessful_attempt_time']) {
			// After 5 unsuccessful attempts to login, block the account for 5 minutes.
			$blockedUntil = $account['last_unsuccessful_attempt_time'] + 300;
			if ($account['unsuccessful_attempt_count'] > 4 && $blockedUntil > time()) {
				$this->msg->error('auth.error-accountBlockedUntil', format::timestamp($blockedUntil));
				$this->redirectBack();
				return;
			}
		}
		
		$passwordHash = auth::passwordHash($formData['userLogin'], $formData['userPassword']);
		if (! $person || $person['password'] != $passwordHash) {
			if ($person) {
				// Increase the number of unsuccessful logins
				$account['unsuccessful_attempt_count'] = $account['unsuccessful_attempt_count'] + 1;
				$account['last_unsuccessful_attempt_time'] = time();
				$this->dao->update('account', $account, 'person_id');
			}
			logger::securityInfo('User with login ' . $formData['userLogin'] . ' failed to log in.');
			$this->msg->error('public.error-loginFailed');
			$this->redirectBack();
			return;
		}
		if ($person['is_blocked']) {
			logger::securityInfo('Attempting to log in a blocked account - ' . $formData['userLogin'] . '.');
			$this->msg->error('auth.error-accountBlocked');
			$this->redirectBack();
			return;
		}
		
		// Clear unsuccessful attemts and write the login time.
		$account['unsuccessful_attempt_count'] = 0;
		$account['last_login_time'] = time();
		$this->dao->update('account', $account, 'person_id');
		
		$person['country'] = $this->dao->getByCode('country', $person['country_code']);
		
		// Load organization where the person is a representative or an organizer.
		$person['organizerList'] = $this->dao->queryAll('SELECT * FROM organization WHERE organizer_id = :person_id', array('person_id' => $person['id']));
		$person['representativeList'] = $this->dao->queryAll(
				'SELECT org.* FROM organization_rep rep ' .
				'JOIN organization org ON rep.organization_id = org.id ' .
				'WHERE rep.person_id = :person_id', array('person_id' => $person['id']));
		foreach (array('organizerList', 'representativeList') as $listName) {
			foreach ($person[$listName] as &$organization) {
				$organization['country'] = $this->dao->getByCode('country', $organization['country_code']);
			}
		}
		
		if (count($person['organizerList']) > 0) {
			$this->getSessionData()->setAuthOrg($person['organizerList'][0]);
			$this->getSessionData()->setAuthOrgRole(auth::$ORG_ROLE_ORGANIZER);
		}
		else if (count($person['representativeList']) > 0) {
			$this->getSessionData()->setAuthOrg($person['representativeList'][0]);
			$this->getSessionData()->setAuthOrgRole(auth::$ORG_ROLE_REPRESENTATIVE);
		}
		
		// Reception authorization
		$seminarList = $this->dao->queryAll(
				'SELECT * FROM seminar s ' .
				'JOIN receptionist r ON r.seminar_id = s.id AND r.person_id = :person_id ' .
				'WHERE s.begin_date <= :now ' .
				'AND s.end_date >= :now',
				array('now' => sql::$TODAY, 'person_id' => $person['id']));
		if (count($seminarList) > 0) {
			$person['receptionSeminar'] = $this->dao->fetchSeminar($seminarList[0]['id'], $person['preferred_language']);
		}
		else {
			// FIXME: this is only for the development
			if (! IN_PRODUCTION && $person['is_admin']) {
				$person['receptionSeminar'] = $this->dao->fetchSeminar(21, $person['preferred_language']);
			}
		}
		
		$this->getSessionData()->setAuthUser($person);

		logger::securityInfo('Successfully logged in.');

		if ($person['is_admin']) {
			$this->redirectWithLanguage('/admin/adminSpace', $person['preferred_language']);
			return;
		}
		if (count($person['organizerList']) + count($person['representativeList']) > 0) {
			$this->redirectWithLanguage('/user/userSpace/welcome', $person['preferred_language']);
			return;
		}
		$this->redirectWithLanguage('/user/userSpace', $person['preferred_language']);
	}
	
	public function switchOrganization() {
		$id = $this->input->get('id');
		if (! $id) {
			$id = $this->input->post('organization_id');
		}

		if (! valid::isId($id)) {
			$this->msg->error('error.invalidId');
			$this->redirectBack();
		}
		$organization = $this->dao->getOrganization($id);
		if (! $organization) {
			$this->msg->error('error.recordNotFound');
			$this->redirectBack();
			return;
		}
		// Find out if the user is an organizer or a representative.
		if ($organization['organizer_id'] == $this->getAuthUser('id') || $this->getAuthUser('is_admin')) {
			$this->getSessionData()->setAuthOrgRole(auth::$ORG_ROLE_ORGANIZER);
		}
		else {
			$isRep = $this->dao->queryCount('SELECT count(*) FROM organization_rep WHERE organization_id = :organization_id AND person_id = :person_id', 
					array('organization_id' => $id, 'person_id' => $this->getAuthUser('id')));
			if (! $isRep) {
				$this->msg->error('auth.accessDenied-organization');
				$this->redirectBack();
				return;
			}
			$this->getSessionData()->setAuthOrgRole(auth::$ORG_ROLE_REPRESENTATIVE);
		}
		$this->getSessionData()->setAuthOrg($organization);
		$this->redirect('/org/orgSpace/welcome');
	}

	public function logout() {
		$redirectPage = '/page';
		if ($this->getAuthUser()) {
			// Kohana::log('info', 'User ' .  $this->getAuthUser('login') . ' logged out.');
		}
		SessionData::reset();
		$this->redirect($redirectPage);
	}

	public function suggestLogin() {
		$person = $_GET;
		// $this->postToDbValues();
		if (! is_array($person)) {
			die('');
		}
		$login = auth::suggestLogin($person, $this->dao);
		
		// Question mark means that some characters were not successfully translated.
		if (strpos($login, '?') !== FALSE) {
			$login = '';
		}

		die($login);
	}
	
	public function newPasswordForm() {
		$token = $this->input->get('token');
		$person = $this->newPasswordValidateToken($token);
		 
		$this->viewData['formData'] = array(
				'full_name' => format::personFullName($person),
				'email' => $person['email'],
				'login' => $person['login'],
				'token' => $token);
		$this->renderSubview('auth/newPassword');
	}
	
	private function newPasswordValidateToken($token) {
		if (! $token) {
			$this->redirect('/page');
		}
		$person = $this->dao->getPersonByPasswordToken($token);
		if (! $person) {
			$this->redirect('/page');
		}
		$this->viewData['person'] = $person;
		return $person;
	}
	
	public function newPasswordSave() {
		$formData = $this->postToDbValues();
		$person = $this->newPasswordValidateToken($formData['token']);
		
		if (! $formData['new_password']) {
			$this->viewData['formData'] = $formData;
			$this->msg->info('error-required', text::get('person-new_password'));
			$this->renderSubview('auth/newPassword');
			return;
		}
		
//	 	if ($formData['newPassword'] != $formData['newPasswordCopy']) {
//	 		$this->viewData['formData'] = $formData;
//	 		$this->msg->info('auth-newPassword-passwordsDiffer');
//	 		$this->renderSubview('auth/newPassword');
//	 		return;
//	 	}
		
		$account = $this->dao->getAccount($person['id']);
		$account['password'] = auth::passwordHash($person['login'], $formData['new_password']);
		// TODO does this work, when I set the field to NULL?
		$account['change_password_token'] = NULL;
		$this->dao->update('account', $account, 'person_id');
		
		$this->msg->info('auth-newPassword-success', $person['login']);
		$this->redirect('/page');
	}
	
	public function forgottenPasswordForm() {
		$this->renderSubview('/auth/forgottenPassword');
	}
	
	public function forgottenPasswordSendMail() {
		$formData = $this->postToDbValues();
		$this->viewData['formData'] = $formData;
		$emailOrLogin = $formData['emailOrLogin'];
		
		if (! $emailOrLogin) {
			$emailOrLogin = $this->input->get('emailOrLogin');
		}
	
		if (valid::isEmpty($emailOrLogin)) {
			$this->msg->error('error.required', 'public.emailOrLogin');
		}
		else {
			if (! $formData['recaptcha_response_field']) {
				$this->msg->error('error.required', 'public.captcha');
			}
			else {
				# The response from reCAPTCHA
				$resp = recaptcha_check_answer(Kohana::config('yosemin.recaptchaPrivateKey'),
						$_SERVER["REMOTE_ADDR"],
						$_POST["recaptcha_challenge_field"],
						$_POST["recaptcha_response_field"]);
				
				if (! $resp->is_valid) {
					# Set the error code so that we can display it.
					// $error = $resp->error;
					$this->msg->error('error.invalidCaptcha');
				}
			}
		}
		
		if ($this->msg->hasErrors()) {
			$this->renderSubview('/auth/forgottenPassword');
			return;
		}
		
		$accountList = array();
		if (valid::isEmail($emailOrLogin)) {
			$accountList = $this->dao->queryAll('SELECT p.*, acc.login, acc.password, acc.is_blocked ' .
					'FROM person p ' .
					'JOIN account acc ON acc.person_id = p.id ' .
					'WHERE email = :email ' .
					'ORDER BY birth_date ASC',
					array('email' => $emailOrLogin));
			if (count($accountList) == 0) {
				$this->msg->error('public.accountByEmailNotFound');
			}
			else {
				$person = $accountList[0];
			}
		}
		else {
			$person = $this->dao->fetchPersonByLogin($emailOrLogin);
			if (! $person) {
				$this->msg->error('public.accountByLoginNotFound');
			}
			else {
				$accountList[] = $person;
			}
		}
	
		if ($this->msg->hasErrors()) {
			$this->renderSubview('forgottenPassword');
			return;
		}
	
		$token = sha1($person['login'] . rand(1, getrandmax()) . time());
		
		$newPassword = substr(md5(rand(10000, 1000000)), 0, 8);
	
		// Change the user's password
		
		$account = $this->dao->getAccount($person['id']);
		$account['change_password_token'] = $token;
		$this->dao->update('account', $account, 'person_id');
	
		$protocol = 'http';
		if ($_SERVER['HTTPS']) {
			$protocol .= 's';
		}
		
		$url = $protocol . '://' . $_SERVER['HTTP_HOST'] . url::site('/auth/newPasswordForm');
		$url .= '?token=' . urlencode($token); 
		
		// Create a body of the mail
		// TODO change the content of the email
		$emailLang = $person['preferred_language'];
		$details = '';
		for ($i = 0; $i < count($accountList); $i++) {
			$person = $accountList[$i];
			if ($i > 0) {
				$details .= "\n--------------------------------------------";
			}
			$details .= text::getInLang($emailLang, 'person.login') . ': ' . $person['login'] . "\n";
			$details .= text::getInLang($emailLang, 'person.full_name') . ': ' . $person['first_name'] . ' ' . $person['last_name']  . "\n";
			$details .= text::getInLang($emailLang, 'auth-passwordChangeLink') . ': <a href="' . $url . '">' . $url . "</a>\n";
		}
		
		$message = text::getInLang($emailLang, 'public.forgottenPasswordEmail-body', $details);
		$message = email::addHeadingAndClosing($message, $person);
		$message = html::wrapInHtml(nl2br($message));
	
		// Send the mail
		$person = $accountList[0];
		$mailResult = email::sendInUtf8(
				$person['first_name'] . ' ' . $person['last_name'],
				$person['email'],
				text::getInLang($emailLang, 'public.forgottenPasswordEmail-subject'),
				$message, TRUE);
		if ($mailResult == email::$SUCCESS) {
			$this->msg->info('public.emailSentSuccessfully', format::maskEmail($person['email']));
		}
		$this->redirect('/page');
	}
}